const fs = require('fs');
const path = require('path');
const Koa = require('koa');
const koaConnect = require('koa-connect');
const colors = require('colors');

const SERVER_PORT = 3000;
const SERVER_HTML_ERROR = 'server_html_error'


const isProd = process.env.NODE_ENV === 'production';
async function createServer() {
    const resolve = (path) => (path.resolve(__dirname, path));
    const app = new Koa();
    let vite = null;
    // 区分环境
    if (!isProd) {
        // 中间件
        vite = await (require('vite')).createServer({
            server: {
                middlewareMode: 'ssr',
            }
        });
        app.use(koaConnect(vite.middlewares));
    } else {
        // 生产环境
        // 压缩代码
        app.use(require('koa-compress'));
        // 启动静态服务器
        app.use(require('koa-static'))(
            resolve('dist/client', {
                index: false,
            })
        )
    }

    // 处理返回客户端的html
    app.use(async (context, next) => {
        const { request } = context;
        const { url } = request;


        try {
            let template, render;
            if (!isProd) {
                template = fs.readFileSync(
                    path.resolve(__dirname, 'index.html'),
                    'utf-8'
                );
                // 2. 应用 Vite HTML 转换。
                //    这将会注入 Vite HMR 客户端，
                //    同时也会从 Vite 插件应用 HTML 转换。
                //    例如：@vitejs/plugin-react 中的 global preambles
                template = await vite.transformIndexHtml(url, template);
                // 3. 加载服务端入口。
                //    vite.ssrLoadModule 将自动转换
                //    你的 ESM 源码使之可以在 Node.js 中运行！无需打包
                //    并提供类似 HMR 的根据情况随时失效。
                render = (await vite.ssrLoadModule('/src/entry-server.tsx')).render;
            } else {
                // 生产模式
                //读取打包的模板
                template = fs.readFileSync(resolve('dist/client/index.html'), 'utf-8');
                //读取打包的服务端入口
                render = (await require(resolve('dist/server/entry-server.js'))).render;
            }

            // 4. 渲染应用的 HTML。这假设 entry-server.js 导出的 `render`
            //    函数调用了适当的 SSR 框架 API。
            //    例如 ReactDOMServer.renderToString()
            const ctx = {};
            const appHtml = await render(url, ctx);

            // 5. 注入渲染后的应用程序 HTML 到模板中。
            const html = template.replace(`<!--app-html-->`, appHtml)

            // 6. 返回渲染后的 HTML。
            context.body = html
            context.status = 200
        } catch (error) {
            if (!IS_PROP){
                // 如果捕获到了一个错误，让 Vite 来修复该堆栈，这样它就可以映射回
                // 你的实际源码中。
                vite.ssrFixStacktrace(e)
            }
            ctx.app.emit('error',new Error(SERVER_HTML_ERROR), context, error);
        }
    })


    app.on('error',(err, ctx, e) => {
        if (err.message === SERVER_HTML_ERROR){
            //打印错误
            const msg = `[返回HTML页面异常]: ${e.stack}`;
            console.error(colors.red(msg));
            ctx.status = 500;
            ctx.body = msg;
        } else {
            const msg = `[服务器异常]: ${e}`;
            console.error(colors.red(msg));
            ctx.status = 500;
            ctx.body = msg;
        }
    });
    
    app.listen(SERVER_PORT, ()  => {
        console.log(
            colors.green('[React SSR]启动成功, 地址为:'),
            colors.green.underline(`http://localhost:${SERVER_PORT}`),
        );
    });
}

createAppServer();
